unit ImpactSprite;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Dialogs, AsphyreFonts, Asphyre2D, AsphyreCanvas, AsphyreDevices,
  AsphyreImages, AsphyreTextures, AsphyreMouse, AsphyreDef, AsphyreKeyboard,
  AsphyreSprite, Math, DirectInput,  Vectors2, vector, AI, NPC_Storage,
  Weapon_Storage;

type
  TStaticObject = class(TPhisicObject)
  private
    procedure Move(const MoveCount: Single); override;
    procedure Draw; override;
    procedure OnCollision(const Sprite: TSprite); override;
  public
    Material:byte;
    Undestructable:boolean;
    Firing:boolean;
    FireSpeed:single;
    DeathCount,incDeath:single;
    NoCollisionBullet:boolean;
    NoCollisionPlayer: boolean;
    CollisionStart,CollisionEnd:single;
    MassStart,MassEnd:single;
    MassInc:boolean;
    CollRadInc:boolean;
    CR:boolean;

    Scaling:boolean;
    Coloring:boolean;
    StartRed,StartGreen,StartBlue,StartAlpha:byte;
    EndRed,EndGreen,EndBlue,EndAlpha:byte;
    StartScaleX,EndScaleX,StartScaleY,EndScaleY:single;

    Particles:boolean;
    ParticlesSpeed:single;
    ParticleImage:string;
    ParticleSColor:cardinal;
    ParticleEColor:cardinal;
    ParticleSpeed:single;
    ParticleDeathSpeed:single;
    ParticleSScaleX,ParticleSScaleY:single;
    ParticleEScaleX,ParticleEScaleY:single;
    ParticleRandomX:single;
    ParticleFx:cardinal;
    constructor Create(AParent:TSpriteEngine;ax,
      ay:real;AVisible:boolean; ST:string; AFiring:boolean=
      false; AFireSpeed:real=0; ADeathCount:real=0; AincDeath:real=0); virtual;
    procedure Hit(Damage:real;HitPoint:TPoint2;HitType:integer;
      WeaponType:byte;Players:boolean; ADirection:real);
  end;

  TBullet = class(TPhisicObject)
  private
    RandValue:double;
    FWeapon:TWeapon;
    procedure Draw;override;
    procedure Move(const MoveCount: Single); override;
    procedure OnCollision(const Sprite: TSprite); override;
    procedure SetWeapon(const Value:TWeapon);
  public
    Sender:TPhisicObject;
    MissionSprite:TSprite;
    Accuracy:double;
    Direction:single;
    StartPoint:TPoint2;
    stopped:boolean;
    Tick:single;
    Speed:single;
    constructor Create(const AParent: TSpriteEngine; AWeapon:TWeapon; ax,
      ay:real; AAngle:real; RandomCurrent:real; ASender:TPhisicObject;
      PVector:TPoint2); virtual;
    property Weapon: TWeapon read FWeapon write SetWeapon;
  end;

  TMovingObject = class(TPhisicObject)
  private
    Group:string;
    Material:byte;
    Poisoned:boolean;
    PoisonSpeed:single;
    IsRun:byte;
    function GetPartsCount:integer;
    procedure Draw; override;
    procedure Move(const MoveCount: Single); override;
    procedure OnCollision(const Sprite: TSprite); override;
  public
    Enemies:TStringList;
    AnimStyle:byte;
    Parts:array of TMovingPart;
    SkeletLine:array[0..15] of TPoint2;
    EnemySprite:TSprite;
    procedure AddPart(APart:TMovingPart);
    procedure DeletePart(Numb:integer);
    procedure SetAngleToPart(Angle:double);
    function FireAction(PartNumb:integer):boolean;
    property PartCount:integer read GetPartsCount;
  end;

  TPlayer = class(TMovingObject)
  private
    procedure Draw; override;
    procedure Move(const MoveCount: Single); override;
    procedure SetWeapon(const Value:TWeapon);
    function GetWeapon:TWeapon;
    procedure OnCollision(const Sprite: TSprite); override;
  public
    Speed:single;
    LeftRight:boolean;

    PlasmaBall:TBullet;

    //rpg
    Strength:byte;
    Agility:byte;
    Accurance:byte;
    Intillegence:byte;
    FreePoint:byte;

    Vitality:single;

    ActivePower:integer;

    Perk:array[0..29] of boolean;
    Skills:array[0..9] of byte;
    PreSkills:array[0..9] of cardinal;
    Powers:array[0..9] of integer;

    Experience,ExperienceMax:real;
    Level,PerkRiched:integer;
    EffectWait:single;

    //view
    Killed:boolean;
    constructor Create(const AParent: TSpriteEngine); override;
    property Weapon: TWeapon read GetWeapon write SetWeapon;
  end;

  TNPC = class(TMovingObject)
  private
    FMission:TPoint2;
    FBrainStatus:TBrainStatus;
    FStoppedTime:single;
    procedure Draw; override;
    procedure Move(const MoveCount: Single); override;
    procedure OnCollision(const Sprite: TSprite); override;
  public
    AI:TAI;
    StaticConstants:TNPC_Static;
    WaitUntilRotate:boolean;

    CreateCurrent:single;
      //view
    Stopping:boolean;
    constructor Create(AParent:TSpriteEngine; ax,ay:real; AName:string); virtual;
    procedure Hit(Damage:real;HitPoint:TPoint2;HitType:integer;
          WeaponType:byte;Players:boolean; ADirection:real);
    procedure SetBrainStatus(const Value:TBrainStatus);
  published
    property Mission:TPoint2 read FMission write FMission;
    property BrainStatus:TBrainStatus read FBrainStatus write SetBrainStatus;
    property StoppedTime:single read FStoppedTime write FStoppedTime;
  end;

const
  M_Meat=0;
  M_Metal=1;
  M_Wood=2;
  M_Plastic=3;

procedure DoExplosion(ax,ay:real; ExplosionRadius,
   Force:real; Damage:real; WeaponType: cardinal);

implementation

uses
Main, Supply, Constants, Geometry, BoxClasses, PathFinder, Paths, Scenes;

function TMovingObject.GetPartsCount:integer;
begin
  Result:=length(Parts);
end;

procedure TMovingObject.AddPart(APart:TMovingPart);
var
c:integer;
begin
  c:=length(Parts);
  SetLength(Parts,c+1);
  Parts[c]:=APart;
end;

procedure TMovingObject.DeletePart(Numb:integer);
var
i,c:integer;
begin
  c:=length(Parts);
  Parts[Numb].PartWeapon.Free;
  for i := Numb to c-2 do
  Parts[i]:=Parts[i+1];
  SetLength(Parts,c-1);
end;

procedure TMovingObject.SetAngleToPart(Angle:double);
var
i:integer;
begin
  for i := 0 to PartCount-1 do
  begin
    Parts[i].Direction:=Angle;
  end;
end;

function TMovingObject.FireAction(PartNumb:integer):boolean;
var
i:integer;
f:real;
rp1,rp2,outp1,outp2:TPoint2;
begin
  Result:=false;
  with Parts[PartNumb] do
  if Weaponed then
  if (not(PartWeapon.Countness)or(Count>0)) then
  FireCurrent:=FireCurrent+PartWeapon.FireSpeed*dt*dt;
  if Parts[PartNumb].Weaponed then
  with Parts[PartNumb] do
  while (FireCurrent>=1)and(not(PartWeapon.Countness)or(Count>0)) do
  begin
    FireCurrent:=FireCurrent-1;
    if (PartWeapon.Countness) then
    dec(Count);
    if Firing then
    Fire:=1;
    RandomCurrent:=RandomCurrent+PartWeapon.RandomAccelerate;
    RandomCurrent:=Min(RandomCurrent,PartWeapon.RandomMax);
    if PartWeapon.Melee then
    begin
      for i := 0 to Engine.Count-1 do
      if Engine[i] is TPhisicObject then
      if Engine[i]<>self then
      if Engine[i].DoCollision then
      begin
        rp1:=VecAdd2(Point2(x,y),VecRotate2(Pos,-angle));
        rp2:=VecAdd2(Point2(x,y),
          Point2(sin256(Direction)*PartWeapon.MeleeRange,
          -cos256(Direction)*PartWeapon.MeleeRange));
        if ((Engine[i].CollideMethod=cmRadius)and
          (LineWithSphere(rp1,rp2,Point2(Engine[i].X,Engine[i].Y),
          Engine[i].CollideRadius,outp1,outp2)>0))or
          ((Engine[i].CollideMethod=cmRect)and
          LineOverlapRect(rp1,rp2,Rect2(Engine[i].CollideRect.left,
          Engine[i].CollideRect.top,Engine[i].CollideRect.Right,
          Engine[i].CollideRect.Bottom))) then
          begin
            with TBullet.Create(Engine,PartWeapon,rp2.x,rp2.y,angle,0,
              self,Point2(0,0)) do
            begin
              MissionSprite:=EnemySprite;
              OnCollision(Engine[i]);
              Dead;
            end;
            break;
          end;
      end;
    end
    else
    begin
      for i := 1 to PartWeapon.BCount do
      with TBullet.Create(Engine,PartWeapon,
        x+VecRotate2(Pos,-angle).x
        +FiringPos.x*sin256(Direction)-FiringPos.y*
        sin256((Direction)+64),
        y+VecRotate2(Pos,-angle).y-FiringPos.x*
        cos256((Direction))+FiringPos.y*
        cos256((Direction)+64),
        (Direction+128)/40.7,RandomCurrent,self,Self.Vector) do
        begin
          if self is TNPC then
          Accuracy:=TNPC(Self).StaticConstants.Accuracy
          else
          if self is TPlayer then
          Accuracy:=TPlayer(Self).Accurance/10;
          MissionSprite:=EnemySprite;
        end;
    end;
    Result:=true;
  end;
end;

procedure TMovingObject.Draw;
var
i:integer;
zx,zy:single;
begin
  if ImageName<>'' then
  inherited Draw;
  for i := 0 to PartCount-1 do
  if (X > Engine.WorldX + VisibleArea.Left) and
    (Y > Engine.WorldY + VisibleArea.Top) and
    (X < Engine.WorldX + VisibleArea.Right) and
    (Y < Engine.WorldY + VisibleArea.Bottom) then
  if Parts[i].ImageName<>'' then
  begin
    Engine.Canvas.DrawRotateC(Engine.Image.Image[Parts[i].ImageName],
      trunc(Parts[i].AnimPos),
      x-Engine.WorldX+VecRotate2(Parts[i].Pos,-angle).x,
      y-Engine.WorldY+VecRotate2(Parts[i].Pos,-angle).y,
      (Parts[i].Direction+Parts[i].DrawAddAngle)/40.7,
      Parts[i].ScaleX,Parts[i].ScaleY,
      cRGB4(Red,Green,Blue),Parts[i].DrawFx);
  end;

  for i := 0 to PartCount-1 do
  if (X > Engine.WorldX + VisibleArea.Left) and
    (Y > Engine.WorldY + VisibleArea.Top) and
    (X < Engine.WorldX + VisibleArea.Right) and
    (Y < Engine.WorldY + VisibleArea.Bottom) then
  if Parts[i].Weaponed then
  if Parts[i].ImageName<>'' then
  if Parts[i].Firing then
  if Parts[i].Fire>0 then
  with Parts[i] do
  begin
    zx:=self.X-Engine.WorldX+VecRotate2(Pos,-angle).x
      +FiringPos.x*sin256(Direction)-FiringPos.y*
      sin256((Direction)+64);
    zy:=self.y-Engine.WorldY+VecRotate2(Pos,-angle).y
      -FiringPos.x*cos256((Direction))-FiringPos.y*
      cos256((Direction)+64);
    Engine.Canvas.DrawRot(Engine.Image.Image['flash'],zx,zy,
      (Direction)/40.7,1,0,fxAddX);
  end;
end;

procedure TMovingObject.OnCollision(const Sprite: TSprite);
var
a:real;
i:integer;
m:integer;
begin
  if (Sprite is TBullet) then
  with TBullet(Sprite) do
  begin
    if Sender=Self then
    exit;
    if (RandValue<Min(Accuracy/dt,1-0.0000000000000000001))or(Accuracy=1) then
    if MissionSprite<>nil then
    if MissionSprite<>Self then
    if FWeapon.WeaponClass=WC_Gun then
    exit;
  end;
  inherited;
end;

procedure TMovingObject.Move(const MoveCount: Single);
var
i:integer;
begin
  inherited;
  for i := 0 to PartCount-1 do
  with Parts[i] do
  begin
    if Weaponed then
    begin
      if Firing then
      Fire:=Fire-0.3*MoveCount;
      
      if RandomCurrent>0 then
      RandomCurrent:=RandomCurrent-0.1*MoveCount;

      RandomCurrent:=Max(RandomCurrent,0);

      if (Count=0) and PartWeapon.Countness then
      begin
        ReloadCurrent:=ReloadCurrent+0.1*MoveCount*MoveCount;
        if ReloadCurrent>=PartWeapon.Reload then
        begin
          ReloadCurrent:=0;
          Count:=PartWeapon.Count;
        end;
      end;
    end;
  end;
  for i := 0 to PartCount-1 do  
  if Parts[i].DoAnimate then
  begin
    case Parts[i].AnimPlayMode of
      pmForward: //12345 12345  12345
      begin
        Parts[i].AnimPos := Parts[i].AnimPos + Parts[i].AnimSpeed * MoveCount;
        if (Parts[i].AnimPos > Parts[i].AnimStart + Parts[i].AnimCount ) then
        begin
          if Parts[i].AnimLooped then Parts[i].AnimPos := Parts[i].AnimStart
          else
          begin
            Parts[i].AnimPos := Parts[i].AnimStart + Parts[i].AnimCount-1 ;
            Parts[i].DoAnimate := False;
          end;
        end;
      end;
         pmBackward: //54321 54321 54321
      begin
        Parts[i].AnimPos := Parts[i].AnimPos - Parts[i].AnimSpeed * MoveCount;
        if (Parts[i].AnimPos < Parts[i].AnimStart) then
        if Parts[i].AnimLooped then
        Parts[i].AnimPos := Parts[i].AnimStart + Parts[i].AnimCount - 1
        else
        begin
          Parts[i].AnimPos := Parts[i].AnimStart;
          Parts[i].DoAnimate := False;
        end;
      end;
         pmPingPong: // 12345432123454321
      begin
        Parts[i].AnimPos := Parts[i].AnimPos + Parts[i].AnimSpeed * MoveCount;
        if Parts[i].AnimLooped then
        begin
          if (Parts[i].AnimPos > Parts[i].AnimStart + Parts[i].AnimCount -1)
            or (Parts[i].AnimPos < Parts[i].AnimStart) then
          Parts[i].AnimSpeed := -Parts[i].AnimSpeed;
        end
        else
        begin
          if (Parts[i].AnimPos > Parts[i].AnimStart + Parts[i].AnimCount) or
            (Parts[i].AnimPos < Parts[i].AnimStart) then
          Parts[i].AnimSpeed := -Parts[i].AnimSpeed;
          if (Trunc(Parts[i].AnimPos)) = (Parts[i].AnimStart +
             Parts[i].AnimCount) then
           Parts[i].DoFlag1 := True;
          if (Trunc(Parts[i].AnimPos) =
             Parts[i].AnimStart) and (Parts[i].DoFlag1) then
           Parts[i].DoFlag2 := True;
          if (Parts[i].DoFlag1) and (Parts[i].DoFlag2) then
          begin
            Parts[i].DoAnimate := False;
            Parts[i].DoFlag1 := False;
            Parts[i].DoFlag2 := False;
          end;
        end;
      end;
    end;
  end;
end;

constructor TPlayer.Create(const AParent: TSpriteEngine);
var
i:integer;
begin
  inherited Create(AParent);
  ImageName:='legs';
  Visible:=true;
  Name:='player';
  SetAnim(ImageName,0,14,0,true);
  Mass:=80;
  AddPart(MovingPart(Point2(0,0),Point2(30,4),0,10,true,'Hands','Man',
    false,0,504 div 36,0.3,pmForward,0));
  Parts[0].Firing:=true;
  Accurance:=1;
  Agility:=1;
  Intillegence:=1;
  Strength:=1;

  for i := 0 to 9 do
  Skills[i]:=0;
  ActivePower:=0;

  Engine.WorldX := -512;
  Engine.WorldY := -384;

  DrawMode:=1;
  MirrorY:=true;

  x:=0;
  y:=0;

  Health:=100;
  CollidePos:=Point2(x,y);

  DoCollision:=true;
  CollideRadius:=20;
  ChangeCollideAttr(cmRadius,CollideRadius,CollideRadius,Mass);
  Z:=-2;
  Parts[0].Count:=Weapon.Count;

  ExperienceMax:=1000;
end;

function TPlayer.GetWeapon:TWeapon;
begin
  result:=Parts[0].PartWeapon;
end;

procedure TPlayer.SetWeapon(const Value:TWeapon);
var
i:pointer;
begin
  if Parts[0].PartWeapon<>nil then
  Parts[0].PartWeapon.Free;
  Parts[0].PartWeapon:=TWeapon.Create;
  Parts[0].PartWeapon.CopyValuesFrom(Value);
  Parts[0].PartWeapon.RandomAccelerate:=Parts[0].PartWeapon.RandomAccelerate/Power(Accurance,0.1);
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*power(Accurance,0.1);
  Parts[0].PartWeapon.Reload:=Parts[0].PartWeapon.Reload/power(Agility,0.1);

  if Skills[Parts[0].PartWeapon.WeaponType]>0 then
  begin
    Parts[0].PartWeapon.RandomAccelerate:=Parts[0].PartWeapon.RandomAccelerate/
      Power(Skills[Parts[0].PartWeapon.WeaponType],0.2);
    Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*power(Skills[Parts[0].PartWeapon.WeaponType],0.1);
    Parts[0].PartWeapon.FireSpeed:=Parts[0].PartWeapon.FireSpeed*
      power(Skills[Parts[0].PartWeapon.WeaponType],0.1);
    Parts[0].PartWeapon.Reload:=Parts[0].PartWeapon.Reload/power(Skills[Parts[0].PartWeapon.WeaponType],0.2);
  end;

  if Perk[0] then
  if Parts[0].PartWeapon.Melee then
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*1.5;

  if Perk[4] then
  if Parts[0].PartWeapon.Stunning=0 then
  begin
    Parts[0].PartWeapon.Stunning:=180;
    Parts[0].PartWeapon.StunningChance:=0.2;
  end;

  if Perk[8] then
  Parts[0].PartWeapon.MaxSpeed:=Parts[0].PartWeapon.MaxSpeed*1.2;
  
  if ((Weapon.WeaponClass=WC_Gun)and(Perk[1]))
  or((Weapon.WeaponClass=WC_Explosion)and(Perk[2]))
  or((Weapon.WeaponClass=WC_Fire)and(Perk[5])) then
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*1.3;

  If (Weapon.WeaponClass=WC_Fire)and(Perk[18]) then
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*1.4;

  if Perk[7] and (Weapon.WeaponClass=WC_Gun) then
  begin
    Parts[0].PartWeapon.Poisoning:=true;
    Parts[0].PartWeapon.PoisonSpeed:=Parts[0].PartWeapon.PoisonSpeed+0.1;
  end;

  if Perk[9] and (Weapon.WeaponClass=WC_Gun) then
  Parts[0].PartWeapon.FlashFire:=true;

  if Parts[0].PartWeapon.Explosive then
  if Perk[2] then
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*1.3;

  if Perk[22] then
  Parts[0].PartWeapon.Optics:=true;

  if Perk[29] then
  if Parts[0].PartWeapon.WeaponClass=WC_Plasma then
  begin
    Parts[0].PartWeapon.SmokeColor1:=clBlue;
    Parts[0].PartWeapon.SmokeColor1:=clBlue;
    Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*1.5;
  end;

  if Perk[20] then
  if Parts[0].PartWeapon.WeaponType=WT_Hands then
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*5;

  if Perk[25] then
  if Parts[0].PartWeapon.WeaponType=WT_Hands then
  begin
    Parts[0].PartWeapon.Poisoning:=true;
    Parts[0].PartWeapon.PoisonSpeed:=Parts[0].PartWeapon.PoisonSpeed+1;
  end;

  if Perk[26] then
  if Parts[0].PartWeapon.WeaponType=WT_Melee then
  begin
    Parts[0].PartWeapon.FeedBack:=Parts[0].PartWeapon.FeedBack+100;
    Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*2;
  end;              

  if Perk[0] then
  if Parts[0].PartWeapon.Melee then
  Parts[0].PartWeapon.Damage:=Parts[0].PartWeapon.Damage*1.5;

  If Perk[17] and (Parts[0].PartWeapon.WeaponClass=WC_Gun) then
  begin
    Parts[0].PartWeapon.FeedBack:=Parts[0].PartWeapon.FeedBack+Min(10*Weapon.Damage/50,10);
  end;
end;

procedure TPlayer.Move(const MoveCount: Single);
var
a:real;
i:integer;
rp:TPoint2;
outp1,outp2:TPoint2;
begin
  inherited Move(MoveCount);
  if MoveCount=0 then
  exit;
  if Killed then
  begin
    If AnimPos<33 then
    AddForce(VecNeg2(VecAngle2b(Point2(0.5*MoveCount,0),Angle+64/40.6)))
    else
    begin
      Dead;
      with TGameScene(Scene) do
      begin
        RenderImageRot(x+2,y+2,angle+pi,ScaleX,ScaleY,ImageName,
          PatternIndex,cRGB4(Red, Green, Blue, 100),fxShadow);
        RenderImageRot(x,y,angle+pi,ScaleX,ScaleY,ImageName,PatternIndex,
          cRGB4(Red, Green, Blue, Alpha),DrawFx);
        Player:=nil;
      end;
    end;
//    MovVector:=Point2(0,0);
    exit;
  end;

  Vitality:=Vitality+0.2*Power(strength,0.2)*MoveCount;
  Vitality:=Min(Vitality,100);
  IsRun:=1;
  if (Weapon.WeaponType<>WT_BigGun)and
    (MainForm.Keyboard.Key[DIK_LSHIFT])and Moving then
  begin
    if ((Vitality>5)or Perk[21]) then
    IsRun:=2;

    Vitality:=Vitality-0.5*MoveCount;
    Vitality:=Max(Vitality,0);
  end;

  Speed:=(3+(Agility-1)/5)*IsRun;

  if Perk[28] then
  Speed:=Speed*1.3;

  MaxHealth:=100+(Strength-1)*25;

  if Perk[16] then
  for i:=0 to Engine.Count do
  if Engine[i] is TNPC then
  if LineLength(Engine[i].x,Engine[i].y,x,y)<150 then
  TNPC(Engine[i]).Health:=TNPC(Engine[i]).Health-MoveCount*0.5*(150-
    LineLength(Engine[i].x,Engine[i].y,x,y))/150;

  if Perk[10] then
  Health:=Health+0.05*MoveCount;

  Health:=Min(Health,MaxHealth);

  Parts[0].Count:=Min(Parts[0].Count,Weapon.Count);

  Moving:=false;

  if MainForm.Keyboard.Key[DIK_W]and MainForm.Keyboard.Key[DIK_A] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,224,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_W]and MainForm.Keyboard.Key[DIK_D] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,32,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_S]and MainForm.Keyboard.Key[DIK_A] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,160,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_S]and MainForm.Keyboard.Key[DIK_D] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,96,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_W] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,0,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_S] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,128,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_A] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,192,5)/40.7;
  end
  else
  if MainForm.Keyboard.Key[DIK_D] then
  begin
    Moving:=true;
    Angle:=MoveToAngle(Angle*40.7,64,5)/40.7;
  end;

  if ActivePower<>1 then
  if PlasmaBall<>nil then
  if not PlasmaBall.IsDead then
  begin
    PlasmaBall.Weapon.TickAcc:=5;
    PlasmaBall:=nil;
  end;

  if Powers[ActivePower]>0 then
  with TGameScene(Scene) do  
  case ActivePower of
  0:
    if MainForm.Mouses.Pressed[1] and (EffectWait<=0) then
    begin
      Motion:=not motion;

      MotionTime:=100;

      if motion then
      begin
        EffectWait:=50;
        First:=true;
      end
      else
      begin
        EffectWait:=1800;
        MotionTime:=2.5;
      end;
    end;
    1:
    begin
      if PlasmaBall = nil then
      begin
        PlasmaBall:=TBullet.Create(Engine,WeaponStorage.Weapon['PlasmaBall'],
          x-sin256(Parts[0].Direction+64)*CollideRadius,
          y+cos256(Parts[0].Direction+64)*CollideRadius,
          0,0,self,Point2(0,0));
        PlasmaBall.Weapon.Damage:=PlasmaBall.Weapon.Damage*sqrt(Powers[ActivePower]);
        PlasmaBall.z:=4;
      end;
      if MainForm.Mouses.Pressed[1] then
      Player.PlasmaBall.Weapon.MissionPoint:=VecAdd2(
        Point2(Mouse.CursorPos.x,Mouse.CursorPos.y),
        Point2(SpriteEngine.WorldX,SpriteEngine.WorldY))
      else
      PlasmaBall.Weapon.MissionPoint:=Point2(x-sin256(Parts[0].Direction+64)*
        CollideRadius*1.5-sin256(Parts[0].Direction-128)*CollideRadius*1.5,y+
        cos256(Parts[0].Direction+64)*CollideRadius*1.5+cos256(Parts[0].Direction-128)*
        CollideRadius*1.5);
    end;
    2:
    if MainForm.Mouses.Pressed[1] then
    if EffectWait<=0 then
    begin
      x:=Mouse.CursorPos.x+Engine.WorldX;
      y:=Mouse.CursorPos.y+Engine.WorldY;
      EffectWait:=600/Powers[ActivePower];
    end;
    3:
    if MainForm.Mouses.Pressed[1] then
    begin
      Moving:=false;
      if EffectWait<=0 then
      begin
        EffectWait:=250;
        for i:=0 to SpriteEngine.Count-1 do
        if Engine[i] is TPhisicObject then
        begin
          if LineLength(x,y,Engine[i].x,Engine[i].y)<
            100*Powers[ActivePower] then
          if not(Engine[i] is TPlayer) then
          begin
            TPhisicObject(Engine[i]).Health:=TPhisicObject(Engine[
              i]).Health-100*(Powers[ActivePower]*100-LineLength(x,y,
              Engine[i].x,Engine[i].y))/(100*Powers[ActivePower]);
           TPhisicObject(SpriteEngine[i]).AddForce(VecScale2(VecNorm2(
              VecSub2(Point2(SpriteEngine[i].x,SpriteEngine[i].y),Point2(x,
              y))),70*Powers[ActivePower]*(100*Powers[ActivePower]-LengthLine(x,y,
              SpriteEngine[i].x,SpriteEngine[i].y))/100*Powers[ActivePower]));
          end;
        end;
        with TStaticObject.Create(SpriteEngine,x,y,true,'Wave',false,0,0,
          10/Powers[ActivePower]) do
        begin
          DoCollision:=false;

          Coloring:=true;
          StartAlpha:=255;
          EndAlpha:=0;
          StartRed:=200;
          StartGreen:=150;
          StartBlue:=150;
          EndRed:=200;
          EndGreen:=55;
          EndBlue:=150;

          Scaling:=true;
          ScaleX:=0;
          ScaleY:=0;
          StartScaleX:=0;
          StartScaleY:=0;
          EndScaleX:=100*Powers[ActivePower]/64;
          EndScaleY:=100*Powers[ActivePower]/64;
          DrawFx:=fxSub;
        end;
      end;
    end;
  end;

  if Moving then
  begin
    DoAnimate:=true;

{    if VecAbs2(MovVector)>(Speed*sqrt(isrun)) then
    MovVector:=VecScale2(VecNorm2(MovVector),Speed*sqrt(isrun));}

    AddForce(VecAngle2b(Point2(0,Speed*GlobalResistance*Mass*Mass),(Angle-64/40.7)));

    Parts[0].AnimPos:=Parts[0].AnimPos+0.4*Speed/3;
    if Parts[0].AnimPos>15 then
    Parts[0].AnimPos:=0;

    if Perk[24] then
    if (not LeftRight and (trunc(AnimPos)=4))or
      (LeftRight and (trunc(AnimPos)=12)) then
    begin
      if LeftRight then
      TBullet.Create(Engine,WeaponStorage.Weapon['SmallFlameGround'],
        OldPos.x-sin256(Angle+64)*7,OldPos.y+cos256(Angle+64)*7,
        (Angle)/40.7,0,self,Point2(0,0))
        else
      TBullet.Create(Engine,WeaponStorage.Weapon['SmallFlameGround'],
        OldPos.x+sin256(Angle+64)*7,OldPos.y-cos256(Angle+64)*7,
        (Angle)/40.7,0,self,Point2(0,0));
      LeftRight:=not LeftRight;
    end;

    AnimPos:=AnimPos+Min(0.8*Speed/3,1);
    if AnimPos>16 then
    AnimPos:=0;
  end
  else
  DoAnimate:=false;

  if Weapon.Optics
    and MainForm.Keyboard.Key[DIK_E] then
  begin
    rp:=VecScale2(VecSub2(Point2(Mouse.CursorPos.x,Mouse.CursorPos.y),
    Point2(512,384)),0.7);
    if VecAbs2(rp)>600 then
    rp:=VecScale2(VecNorm2(rp),600);
    rp:=VecAdd2(rp,Point2(x,y));
    rp:=VecSub2(rp,Point2(512,384));
    Engine.WorldX := MoveToValue(rp.x,Engine.WorldX,
      sqrt(LineLength(rp.x,rp.y,Engine.WorldX,Engine.WorldY))/1.5);
    Engine.WorldY := MoveToValue(rp.y,Engine.WorldY,
      sqrt(LineLength(rp.X,rp.Y,Engine.WorldX,Engine.WorldY))/1.5);
  end
  else
  begin
    Engine.WorldX := MoveToValue(X-512,Engine.WorldX,
      sqrt(LineLength(X-512,Y-384,Engine.WorldX,Engine.WorldY)));
    Engine.WorldY := MoveToValue(Y-384,Engine.WorldY,
      sqrt(LineLength(X-512,Y-384,Engine.WorldX,Engine.WorldY)));
  end;

  Engine.WorldX := max(Engine.WorldX,-1024);
  Engine.WorldX := min(Engine.WorldX,0);

  Engine.WorldY := max(Engine.WorldY,-1024);
  Engine.WorldY := min(Engine.WorldY,1024-768);

  if LineLength(mouse.CursorPos.X,mouse.CursorPos.Y,(x-Engine.WorldX),(y-
    Engine.WorldY))=0 then
  a:=0
  else
  a:=GetAngle((x-Engine.WorldX),(y-Engine.WorldY),mouse.CursorPos.X,
    mouse.CursorPos.Y);

  Parts[0].Direction:=MoveToAngle(Parts[0].Direction,a,Parts[0].AngleSpeed*MoveCount);

  Weapon.MissionPoint:=Point2(Mouse.CursorPos.x+Engine.worldX,
    Mouse.CursorPos.Y+Engine.worldY);

  EnemySprite:=TGameScene(Scene).GetNPC(10);

  if ((Parts[0].Count>0)or not Weapon.Countness) then
  if MainForm.Mouses.Pressed[0] then
  if (IsRun=1)or (Weapon.WeaponType<WT_Pistol) then
  with Parts[0] do
  begin
    if PartWeapon.Melee then
    FireCurrent:=FireCurrent+Weapon.FireSpeed*MoveCount*
        Power((Agility+1)/50,0.5);
    
    if FireAction(0) then
    if Weapon.Melee then
    begin
      Vitality:=Vitality-5;
      Vitality:=Max(Vitality,0);
    end;
  end;

  if Experience>ExperienceMax then
  begin
    ExperienceMax:=ExperienceMax*Max((2-Level/15),1.2);

    inc(Level);
    inc(FreePoint);
    inc(PerkRiched);
  end;

  for i := 0 to 9 do
  if PreSkills[i]>=10000 then
  begin
    inc(Skills[i]);
    Skills[i]:=Min(Skills[i],12);
    SetWeapon(WeaponStorage.Weapon[Weapon.ID]);
    PreSkills[i]:=PreSkills[i]-10000;
  end;

  if Health<0 then
  begin
    Killed:=true;
    DoCollision:=false;
    DoAnimate:=true;
    Angle:=(Parts[0].Direction+128)/40.7;
    MirrorX:=true;
    SetAnim('Man',16,20,0.8,false);
    DeletePart(0);
  end;
end;

procedure TPlayer.Draw;
begin
  inherited Draw;
end;

procedure TPlayer.OnCollision(const Sprite: TSprite);
begin
  if (Sprite is TStaticObject) and (TStaticObject(sprite).NoCollisionPlayer) then
  exit;
  inherited;
end;

constructor TNPC.Create(AParent:TSpriteEngine; ax,ay:real; AName:string);
begin
  inherited Create(AParent);
  Self.X:=ax;
  Self.y:=ay;
  OldPos:=Point2(ax,ay);
  CollidePos:=Point2(ax,ay);
  DoCollision:=true;
  DrawMode:=1;
  NPCStorage.NPCInfo[AName].SetPropertiesToSprite(self);
  AI:=TAI.Create(SpriteEngine,self,1,1);
  Health:=MaxHealth;
  z:=0;
  VisibleArea:=Rect(round(-PatternWidth*ScaleX),round(-PatternWidth*ScaleY),
  round(1024+PatternWidth*ScaleX),round(768+PatternHeight*ScaleY));
end;

procedure TNPC.Draw;    
begin
  if (X > Engine.WorldX + VisibleArea.Left) and
    (Y > Engine.WorldY + VisibleArea.Top) and
    (X < Engine.WorldX + VisibleArea.Right) and
    (Y < Engine.WorldY + VisibleArea.Bottom) then
    if Poisoned then
   Engine.Canvas.DrawEx(Engine.Image.Image['light'],
   x-engine.WorldX-CollideRadius*1.5,
   y-engine.WorldY-CollideRadius*1.5,
   (CollideRadius*3)/128,clGreen4[1],0,fxAdd);
  inherited;
end;

procedure TNPC.Move(const MoveCount:single);
var
i,r: integer;
Direction,a:real;
IsPoint:TPoint2;
p:TPath;
rp:TPoint2;
outp1,outp2:TPoint2;
begin
  IsPoint:=Point2(9999,9999);
  r:=0;
  if (StoppedTime>0)and(Health>0) then
  begin
    StoppedTime:=StoppedTime-1*MoveCount;
    CollidePos:=Point2(x,y);
    exit;
  end;

  inherited;

  if MoveCount=0 then
  exit;

  if StaticConstants<>nil then
  with StaticConstants do
  begin
    if Health<=0 then
    begin
      for i := 0 to PartCount-1 do
      DeletePart(i);
      SetAnim(DeathAnimImage,DeathAnimStart,DeathAnimCount,DeathAnimSpeed,false,
        false,true,pmForward);
      AnimPos:=Max(AnimPos,DeathAnimStart);

      if AnimPos=DeathAnimStart then
      Angle:=Angle+DeathAddAngle/40.7;

      if AnimPos<DeathAnimStart+DeathAnimCount-1 then
      DoCollision:=false
      else
      begin
        dead;
        AI.Free;

        if Player<>nil then
        begin
          Player.Experience:=Player.Experience+
            GetExperience*sqrt(Player.Intillegence);

          if Player.Perk[27] then
          Player.Health:=Player.Health+0.5;
        end;
        with TGameScene(Scene) do
        begin
          RenderImageRot(x+2,y+2,angle,ScaleX,ScaleY,ImageName,
            PatternIndex,cRGB4(Red, Green, Blue, 100),fxShadow);
          RenderImageRot(x,y,angle,ScaleX,ScaleY,ImageName,PatternIndex,
            cRGB4(Red, Green, Blue, Alpha),DrawFx);
        end;
      end;
      Z:=-2;
      Stopping:=true;
      exit;
    end;

    if Poisoned then
    Health:=Health-PoisonSpeed*MoveCount;
    
    AI.Think;

    if Creating then
    if Engine.Count<51 then
    begin
      CreateCurrent:=CreateCurrent+CreateSpeed*MoveCount;
      if CreateCurrent>1 then
      begin
        TNPC.Create(Engine,x,y,CreateName);
        CreateCurrent:=0;
      end;
    end;

    if Health>0 then
    begin
      Direction:=Angle*40.7-AddAngle;
      A:=GetAngle(x,y,Mission.x,Mission.y);
      Direction:=MoveToAngle(Direction,A,AngleSpeed*MoveCount);
      Angle:=(Direction+AddAngle)/40.7;
      if not Stopping then
      AddForce(VecScale2(Point2(Speed*sin256(Direction),
       -Speed*cos256(Direction)),(GlobalResistance*Mass*Mass*MoveCount)));
      for i := 0 to PartCount-1 do
      if Parts[i].AngleSpeed>0 then
      Parts[i].Direction:=MoveToAngle(Parts[i].Direction,A,
        Parts[i].AngleSpeed*MoveCount);
    end;

    Moving:=(((round(A)=round(Direction))or(not WaitUntilRotate))and
      not stopping)and(Health>0);
    DoAnimate:=Moving;

    for i := 0 to PartCount-1 do
    Parts[i].DoAnimate:=Moving;

    for i := 0 to PartCount-1 do
    if (BrainStatus=br_Attack) then
    FireAction(i);

    Stopping:=false;
  end;
end;

procedure TNPC.OnCollision(const Sprite: TSprite);
var
a:real;
i:integer;
m:integer;
begin
  inherited;
end;

procedure TNPC.SetBrainStatus(const Value:TBrainStatus);
begin
{  if (FBrainStatus<>Value)or(not(DoAnimate)and(AttackWait=
    StaticConstants.AttackWaitMax)) then
  begin
    with StaticConstants do
    if ((FBrainStatus<>br_Attack)and(value=br_Attack))
     or ((FBrainStatus=br_Attack)and(value<>br_Attack))
     or(not(DoAnimate)and(AttackWait=AttackWaitMax)) then
    begin
      FBrainStatus:=Value;
      if FBrainStatus=br_Attack then
      begin
        AnimPos:=AttackAnimStart;
        SetAnim(ImageName,AttackAnimStart,AttackAnimCount,AttackAnimSpeed,
         false,false,true,pmPingPong);
      end
      else
      begin
        AnimPos:=0;
        SetAnim(ImageName,0,MoveAnimCount,
         MoveAnimSpeed,true,false,true,pmPingPong);
      end;
    end;
  end;}
  FBrainStatus:=Value;  
end;

procedure TNPC.Hit(Damage:real;HitPoint:TPoint2;HitType:integer;
  WeaponType:byte;Players:boolean; ADirection:real);
var
z:real;
i:integer;
begin
  z:=Health;
  Health:=Health-Damage;

  if Players then
  if WeaponType>-1 then
  Player.PreSkills[WeaponType]:=
    round(Player.PreSkills[WeaponType]+z-Health);

  case Material of
  M_Meat:
    if (HitType<>WC_Fire) then
    case round(Damage) of
    1..150:
      for i := 0 to Min(round(Damage/10),15) do
      with TParticleSprite.Create(SpriteEngine) do
      begin
        x:=HitPoint.x;
        y:=HitPoint.y;
        Line:=true;
        LinePosCount:=7;
        DrawFx:=fxAdd;
        Visible:=false;
        Bezier:=false;
        Angle:=(128+256-GetAngle(self.x,self.Y,x,y)-Min(round(Damage/10),15)*5+i*
          10+random*4-random*4)/40.7;
        VelocityX:=(10*random+2)*sin(Angle)+Vector.x;
        VelocityY:=(10*random+2)*cos(Angle)+Vector.y;
        AccelX:=-VelocityX/40;
        AccelY:=-VelocityY/40;
        LifeTime:=40;
        LineEndColor:=$FFAAAAFF;
        SetColor(255,40,40,00);
        VisibleArea:=Rect(0,0,1024,768);
        Decay:=1;
        UpdateSpeed:=1;
        z:=1;
      end;
    151..300:
      if Health<0 then
      dead;
    end;
  end;
  if not (HitType=WC_Fire) then
  if Damage>0 then
  begin
    if UnStopable then
    begin
      with TGameScene(Scene) do
      begin
        RenderImageRot(x,Y,random*256/40.6,
          1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
          cColorAlpha4(clWhite,round(50*GameSpeed)),fxBlend);
        RenderImageRot(x,Y,random*256/40.6,
          1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
          cColorAlpha4(clWhite,round(50*GameSpeed)),fxBlend);
      end;
    end
    else
    begin
      with TGameScene(Scene) do
      begin
        RenderImageRot(x,Y,random*256/40.6,
          1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
          cColorAlpha4(clWhite,150),fxBlend);
        RenderImageRot(x,Y,random*256/40.6,
          1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
          cColorAlpha4(clWhite,150),fxBlend);
      end;    
    end;

    for i:=0 to round(Min(Damage/20,4)) do
    begin
      with TGameScene(Scene) do
      if UnStopable then
      RenderImageRot(
        x-20*(i+1)*Sin256(ADirection*40.6),Y+20*(i+1)*
        Cos256(ADirection*40.6),
        random*256/40.6,1-random*0.6,1-random*0.6,'blood_decor',0,
        cColorAlpha4(clWhite,Max(0,50*round(GameSpeed)-(i+1)*20)),fxBlend)
        else
      RenderImageRot(
        x-20*(i+1)*Sin256(ADirection*40.6),Y+20*(i+1)*
        Cos256(ADirection*40.6),
        random*256/40.6,1-random*0.6,1-random*0.6,'blood_decor',0,
        cColorAlpha4(clWhite,150-(i+1)*20),fxBlend);
    end;
  end;
end;

constructor TStaticObject.Create(AParent:TSpriteEngine;ax,
  ay:real;AVisible:boolean; ST:string; AFiring:boolean=false; AFireSpeed:real=0; ADeathCount:real=0; AincDeath:real=0);
begin
  inherited Create(AParent);
  x:=ax;
  Y:=ay;
  Visible:=AVisible;
  ImageName:=ST;
  Firing:=AFiring;
  FireSpeed:=AFireSpeed;
  DeathCount:=ADeathCount;
  incDeath:=AincDeath;
  ChangeCollideAttr(CollideMethod,CollideRadius,CollideRadius, 0);

  DrawMode:=1;
  DrawFx:=fxBlend;

  SetPosition(ax,ay);
end;

procedure TStaticObject.Hit(Damage:real;HitPoint:TPoint2;HitType:integer;
  WeaponType:byte;Players:boolean; ADirection:real);
begin
  Health:=Health-Damage;
end;

procedure TStaticObject.Move(const MoveCount: Single);
var
i:integer;
dc:real;
dr,db,dg,da:integer;
dsx,dsy:real;
begin
  Inherited Move(moveCount);

  if MoveCount=0 then
  exit;

  if CollRadInc then
  begin
    dc:=CollisionStart-CollisionEnd;

    CollideRadius:=round(CollisionStart-dc*DeathCount/100);
    ChangeCollideAttr(cmRadius,CollideRadius,CollideRadius, Mass);
  end;

  if MassInc then
  begin
    dc:=MassStart-MassEnd;

    Mass:=round(MassStart-dc*DeathCount/100);
  end;


{  if VecAbs2(Vector)<1.5 then
  Vector:=Point2(0,0);}

  if Firing then
  if random<FireSpeed*MoveCount then
  TBullet.Create(Engine,WeaponStorage.Weapon['SmallFlame'],x,y,random*Pi*2,0,
    nil,Point2(0,0));

  if Particles then
  begin
    if ParticleSpeed>random/MoveCount then
    with TParticleSprite.Create(Engine) do
    begin
      x:=Self.x+sin256(Angle*40.6+random(64)-random(64))
        *ParticleRandomX*random;
      y:=Self.y-cos256(Angle*40.6+random(64)-random(64))
        *ParticleRandomX*random;

      ImageName:=ParticleImage;
      Scaling:=true;
      Coloring:=true;
      ScaleX:=ParticleSScaleX;
      ScaleY:=ParticleSScaleY;
      StartScaleX:=ParticleSScaleX;
      StartScaleY:=ParticleSScaleY;
      EndScaleX:=ParticleEScaleX;
      EndScaleY:=ParticleEScaleY;

      SetColor(ParticleSColor);
      Decay:=ParticleDeathSpeed;

      StartColor:=ParticleSColor;
      EndColor:=ParticleEColor;

      LifeTime:=100;
      StartLifeTime:=100;

      DrawFx:=ParticleFx;
      z:=Self.Z;
    end;
  end;

  if Coloring then
  begin
    dr:=StartRed-EndRed;
    db:=StartBlue-EndBlue;
    dg:=StartGreen-EndGreen;
    da:=StartAlpha-EndAlpha;
    Red:=round(StartRed-dr*DeathCount/100);
    Green:=round(StartGreen-dg*DeathCount/100);
    Blue:=round(StartBlue-db*DeathCount/100);
    Alpha:=round(StartAlpha-da*DeathCount/100);
  end;
   if Scaling then
  begin
    dsx:=StartScaleX-EndScaleX;
    dsy:=StartScaleY-EndScaleY;
    ScaleX:=StartScaleX-dsx*DeathCount/100;
    ScaleY:=StartScaleY-dsy*DeathCount/100;
  end;

  DeathCount:=DeathCount+incDeath*MoveCount;

  VisibleArea:=Rect(round(-PatternWidth*ScaleX),round(-PatternWidth*ScaleY),
    round(1024+PatternWidth*ScaleX),round(768+PatternHeight*ScaleY));

  if DeathCount>100 then
  Dead;
end;

procedure TStaticObject.Draw;
begin
  inherited Draw;
end;

procedure TStaticObject.OnCollision(const Sprite: TSprite);
begin
  if (Sprite is TBullet)and(NoCollisionBullet) then
  exit;
  if (TPlayer(Sprite) is TPlayer)and NoCollisionPlayer then
  exit;
  inherited ;
end;

constructor TBullet.Create(const AParent: TSpriteEngine; AWeapon:TWeapon;
  ax,ay:real; AAngle:real; RandomCurrent:real; ASender:TPhisicObject;
  PVector:TPoint2);
var
  v1:TPoint2;
begin
  inherited Create(AParent);
  Name:='Bullet';
  x:=aX;
  y:=ay;
  OldPos:=Point2(x,y);

  Weapon:=AWeapon;

  Sender:=ASender;

  ImageName:=Weapon.WeaponStyle;

  StaticAngle:=false;

  Weapon.MaxSpeed:=Weapon.MaxSpeed+random*Weapon.RandSpeed/2-random*
    Weapon.RandSpeed/2;

  Direction:=AAngle+random*RandomCurrent/40.6-random*RandomCurrent/40.6;
  Angle:=random*Pi*2;

  if Weapon.Spin=0 then
  Angle:=Direction;

  UnStopable:=Weapon.UnStopable;

  if Weapon.StartImpuls then
  v1:=VecAngle2b(Point2(0,Weapon.MaxSpeed),Direction+pi/2);

  if Not Weapon.MissionByAir then
  if Not Weapon.Laser then
  v1:=VecAdd2(v1,PVector);

  Speed:=VecAbs2(V1);

  if Weapon.StartImpuls or Weapon.MissionByAir then
  Weapon.MaxSpeed:=VecAbs2(V1)
  else
  Weapon.MaxSpeed:=Weapon.MaxSpeed+VecAbs2(PVector);


  StartPoint:=Point2(x,y);
  CollidePos:=Point2(x,y);

  if Weapon.MissionByAir then
  if VecAbs2(VecSub2(StartPoint,Weapon.MissionPoint))>Weapon.MissionMaxLength
  then
  Weapon.MissionPoint:=VecAdd2(StartPoint,VecScale2(VecNorm2(
    VecSub2(Weapon.MissionPoint,StartPoint)),Weapon.MissionMaxLength));

  DrawFx:=Weapon.fxEffect;

  tick:=255;

  CollideRadius:=Weapon.CollisionRadius;
  DoCollision:=Weapon.DoCollision;
  ChangeCollideAttr(cmRadius,CollideRadius,CollideRadius,Weapon.BMass);
  AddForce(VecScale2(v1,Mass));
  if Weapon.Coloring then
  begin
    SetColor(Weapon.SColor);
  end;
  if Weapon.Scaling then
  begin
    ScaleX:=Weapon.SScaleX;
    ScaleY:=Weapon.SScaleY;
  end;
  if not ((Weapon.AnimCount=Weapon.AnimStart)and(Weapon.AnimStart=0)) then
  begin
    DoAnimate:=true;
    AnimPos:=0;
    SetAnim(ImageName,Weapon.AnimStart,Weapon.AnimCount,Weapon.AnimSpeed,true);
  end;

  z:=Weapon.z;
  
  MirrorX:=Weapon.FlipX;
  MirrorY:=Weapon.FlipY;

  SetAngle(Angle);

  SetPosition(ax,ay);

  DrawMode:=1;
end;

procedure TBullet.Draw;
var
Top:integer;
Bottom:integer;
p:real;
z1,z2,z3,z:real;
begin
  if Weapon.Melee then
  exit;
  if Weapon.MissionByAir then
  if (X > Engine.WorldX + VisibleArea.Left) and
  (Y > Engine.WorldY + VisibleArea.Top) and
  (X < Engine.WorldX + VisibleArea.Right) and
  (Y < Engine.WorldY + VisibleArea.Bottom) then
  begin
    z1:=LineLength(x,y,Weapon.MissionPoint.x,Weapon.MissionPoint.y);
    z2:=LineLength(StartPoint.x,StartPoint.y,Weapon.MissionPoint.x,
      Weapon.MissionPoint.y);
    z:=Max(z2/100,0);
    z:=Min(z,7);
    if z1>z2/2 then
    z3:=(z+1)-z*z1/z2
    else
    z3:=1+z*z1/z2;

    Engine.Canvas.DrawRotateC(Engine.Image.Image[ImageName],
    PatternIndex,
    Trunc(X + WorldX + OffsetX - Engine.WorldX)+z3*2,
    Trunc(Y + WorldY + OffsetY - Engine.WorldY)+z3*2,
    Angle, ScaleX, ScaleY,
    MirrorX, MirrorY,
    cRGB4(Red, Green, Blue, 100), fxShadow);
  end;

  if Weapon.Laser then
  begin
    Top:=cColorAlpha1(Weapon.SmokeColor2,
      Alpha);
    Bottom:=cColorAlpha1(Weapon.SmokeColor1,
      Alpha);

    Engine.Canvas.LineWidth(Point2(round(x-engine.WorldX),round(y-engine.WorldY)),
        VecSub2(StartPoint,Point2(Engine.WorldX,Engine.WorldY)),top,bottom,4,
        Weapon.fxEffect);
  end;

  if Weapon.smoking then
  begin
    Top:=cColorAlpha1(Weapon.SmokeColor2,Max(0,
      round(50*Speed/Weapon.MaxSpeed*Alpha/255)));
    Bottom:=cColorAlpha1(Weapon.SmokeColor1,
      round(255*Speed/Weapon.MaxSpeed*Alpha/255));

    if OverlapRect(Rect(round(x-engine.WorldX),round(y-engine.WorldY),
    round(x-Engine.WorldX-VecAngle2b(Point2(Speed*6,0),Angle+64/40.6).x),
    round(y-Engine.WorldY-VecAngle2b(Point2(Speed*4,0),Angle+64/40.6).y)),Rect(0,0,1124,968)) then
    begin
      if Weapon.SmokingLength=0 then
      p:=Speed*6
      else
      p:=Weapon.SmokingLength;
      if p<VecAbs2(Point2(x-StartPoint.x,y-StartPoint.y)) then
      Engine.Canvas.LineWidth(Point2(round(x-engine.WorldX),round(y-engine.WorldY)),
      VecSub2(Point2(x-Engine.WorldX,y-Engine.WorldY),
        VecScale2(VecNorm2(Vector),p)),top,bottom,1.5,Weapon.fxEffect)
      else
      Engine.Canvas.LineWidth(Point2(round(x-engine.WorldX),round(y-engine.WorldY)),
      VecSub2(Point2(x-Engine.WorldX,y-Engine.WorldY),Point2(x-StartPoint.x,
        y-StartPoint.y)),top,bottom,1.5,Weapon.fxEffect);
    end;
  end;
  if not ((Weapon.Smoking)or(weapon.Laser)) then
  inherited draw;
end;


procedure TBullet.Move(const MoveCount:single);
var
  i, m, d: integer;
  z3, z1, z2: real;
  zPoint, p1, p2, p: TPoint2;
  dsx, dsy: real;
begin
  RandValue:=Random;
  if Weapon.Melee then
  exit;
  if not PtInRect(Rect(-1224,-1224,1224,1224),Point(round(x),round(y))) then
  dead;
  inherited;
  Tick:=Max(Tick,0);
  if Weapon.Coloring then
  begin
    if Weapon.Flying then
    SetColor(MixColors(Weapon.SColor,Weapon.EColor,(255-Tick)/255))
    else
    if Weapon.StartImpuls then
    SetColor(MixColors(Weapon.SColor,Weapon.EColor,(Weapon.MaxSpeed-Speed)/
      Weapon.MaxSpeed))
    else
    SetColor(MixColors(Weapon.SColor,Weapon.EColor,(Speed)/Weapon.MaxSpeed));
  end;
  if Weapon.Scaling then
  begin
    dsx:=Weapon.SScaleX-Weapon.EScaleX;
    dsy:=Weapon.SScaleY-Weapon.EScaleY;
    if Weapon.Flying then
    begin
      ScaleX:=Weapon.SScaleX-dsx*(255-Tick)/255;
      ScaleY:=Weapon.SScaleY-dsy*(255-Tick)/255;
    end
    else
    if Weapon.StartImpuls then
    begin
      ScaleX:=Weapon.SScaleX-dsx*(Weapon.MaxSpeed-Speed)/(Weapon.MaxSpeed);
      ScaleY:=Weapon.SScaleY-dsy*(Weapon.MaxSpeed-Speed)/(Weapon.MaxSpeed);
    end
    else
    begin
      ScaleX:=Weapon.SScaleX-dsx*(Speed)/(Weapon.MaxSpeed);
      ScaleY:=Weapon.SScaleY-dsy*(Speed)/(Weapon.MaxSpeed);
    end;
  end;

//  FBody.AngVelocity:=MoveToValue(Weapon.Spin*MoveCount,FBody.AngVelocity,0.1);

  if not Weapon.Flying then
  Speed:=VecAbs2(Vector)/MoveCount;

  VisibleArea:=Rect(-400,-400,1424,1468);

  if Weapon.MissionByAir then
  begin
    z1:=LineLength(x,y,Weapon.MissionPoint.x,Weapon.MissionPoint.y);
    z2:=LineLength(StartPoint.x,StartPoint.y,Weapon.MissionPoint.x,
      Weapon.MissionPoint.y);
    if z2<LineLength(x,y,StartPoint.x,StartPoint.y) then
    Dead;
    z3:=Max(z2/200,0);
    z3:=Min(z3,3);
    if z1>z2/2 then
    begin
      ScaleX:=(z3+1)-z3*z1/z2;
      ScaleY:=(z3+1)-z3*z1/z2;
    end
    else
    begin
      ScaleX:=1+z3*z1/z2;
      ScaleY:=1+z3*z1/z2;
    end;
    z:=round(z3);
  end;

  if Weapon.Flying then
  begin
    Tick:=Tick-Weapon.TickAcc*MoveCount;
    if Tick<0 then
    Dead;
  end;

  if Weapon.Transparenting then
  if not Weapon.Flying then
  Alpha:=round(255*Speed/Weapon.MaxSpeed)
  else
  begin
    Alpha:=round(255*Tick/255);
  end;

  if Weapon.Mission then
  if Weapon.MissionStops then
  if CInC(Weapon.MissionPoint.x,Weapon.MissionPoint.y,1,x,y,CollideRadius,true)
  then
  stopped:=true;

  if Weapon.StartImpuls then
  begin
  end
  else
  begin
    if Weapon.Mission then
    Direction:=GetAngle(Weapon.MissionPoint.x,
      Weapon.MissionPoint.y,x,y)/40.7;
    AddForce(VecAngle2(Point2(0,Weapon.MaxSpeed*GlobalResistance*sqr(Mass)*
      MoveCount),1.5*pi-Direction));
  end;

  if stopped then
  begin
    dead;
{    if Weapon.Laser then
    Vector:=Point2(0,0);}
  end;

  if VecAbs2(Vector)>25 then
  if DoCollision then
  begin
    p:=Point2(x,y);
    d:=-1;
    for i:=0 to Engine.Count-1 do
    if not (Engine[i]=Self) then
    if Engine[i] is TPhisicObject then
    if Engine[i].DoCollision then
    if not (Engine[i] = Sender) then
    if not ((Engine[i] is TBullet) and (TBullet(Engine[i]).Sender=
      Sender)) then
    begin
      if Engine[i].CollideMethod=cmRadius then
      m:=LineWithSphere(OldPos,p,
         Point2(Engine[i].x,Engine[i].y),Engine[i].CollideRadius,p1,p2)
      else
      m:=LineWithQuad(OldPos,p,Engine[i].CollideQuadrangle,p1,p2);
      if not Weapon.UnStopable then
      begin
        if m=2 then
        begin
          if LineLength(p1.x,p1.y,OldPos.x,OldPos.y)<LineLength(p2.x,p2.y,
            OldPos.x,OldPos.y) then
          p:=p1
          else
          p:=p2;
          d:=i;
        end;
      end
      else
      if m=2 then
      begin
        OnCollision(Engine[i]);
        Engine[i].OnCollision(self);
      end;
    end;
    if d>-1 then
    begin
      StartPoint:=Point2(x,y);
      Engine[d].OnCollision(self);
      OnCollision(Engine[d]);
    end;
  end;

  if Weapon.Fire then
  with TGameScene(Scene) do  
  if random<4*Alpha/255*MoveCount*Movecount then
    RenderImageRot(x,Y,0,
    ScaleX*Engine.Image.Image[ImageName].PatternSize.x/50,
    ScaleY*Engine.Image.Image[ImageName].PatternSize.y/50,
    'gar',0,cColorAlpha4(clWhite,
    Max(round(4*Alpha/255*MoveCount),1)),fxSub);

  if Weapon.Particles then
  begin
    if Weapon.ParticleSpeed>random/MoveCount then
    with TParticleSprite.Create(Engine) do
    begin
      x:=Self.x+sin256(Direction*40.6+random(64)-random(64))
        *Weapon.ParticleRandomX*random;
      y:=Self.y-cos256(Direction*40.6+random(64)-random(64))
        *Weapon.ParticleRandomX*random;

      ImageName:=Weapon.ParticleImage;
      Scaling:=true;
      Coloring:=true;
      ScaleX:=Weapon.ParticleSScaleX;
      ScaleY:=Weapon.ParticleSScaleY;
      StartScaleX:=Weapon.ParticleSScaleX;
      StartScaleY:=Weapon.ParticleSScaleY;
      EndScaleX:=Weapon.ParticleEScaleX;
      EndScaleY:=Weapon.ParticleEScaleY;

      SetColor(Weapon.ParticleSColor);
      Decay:=Weapon.ParticleDeathSpeed;

      StartColor:=Weapon.ParticleSColor;
      EndColor:=Weapon.ParticleEColor;

      LifeTime:=100;
      StartLifeTime:=100;

      DrawFx:=Weapon.ParticleFx;
      z:=Self.Z;
    end;
  end;

  if (speed<1)and(Weapon.StartImpuls)and not(Weapon.Flying) then
  dead;

  if stopped and not(Weapon.StartImpuls) then
  dead;

  if IsDead then
  begin
    If Weapon.Explosive then
    DoExplosion(x,y,Weapon.ExplosiveRadius,Weapon.FeedBack,Weapon.Damage,Weapon.WeaponType);

    if Weapon.Firing then
    begin
      for i:=0 to Weapon.FiringCount do
      begin
        z3:=Pi*2/(Weapon.FiringCount+1)*i;
        zPoint:=VecAdd2(Point2(X,Y),VecAngle2(Point2(0,random*
          Weapon.FiringRadius),z3));
        with TStaticObject.Create(Engine,zPoint.x,zPoint.y,false,'',
          true,0.1,0,Weapon.FiringTimeOut) do
        DoCollision:=false;
      end;
    end;
  end;
end;

procedure TBullet.OnCollision(const Sprite: TSprite);
var
  z: real;
  i: integer;
  Modif: real;
begin
  if (dt=0)and (not Weapon.Melee) then
  exit;

  if (Sprite is TMovingObject) then
  if RandValue<Accuracy then
  if MissionSprite<>nil then
  if MissionSprite<>Sprite then
  if FWeapon.WeaponClass=WC_Gun then
  exit;

  if Sprite is TStaticObject then
  if TStaticObject(Sprite).NoCollisionBullet then
  exit;
  if Sprite Is TBullet then
  if (TBullet(Sprite).Sender=Sender) then
  exit;

  if (Sprite=Sender) then
  exit;

  inherited OnCollision(sprite);

  if not UnStopable then
  StartPoint:=Point2(x,y);

  if (UnStopable and Weapon.MissionByAir)and TPhisicObject(Sprite).CollideUnstopable then
  stopped:=true;

  if stopped then
  exit;

  if Weapon.FeedBack>0 then
  TPhisicObject(Sprite).AddForce(Point2(
    VecAngle2b(Point2(0,Weapon.FeedBack),angle+pi/2).x,VecAngle2b(Point2(0,
      Weapon.FeedBack,),angle+pi/2).y));

  StartPoint:=Point2(x,y);

  if Sprite is TBullet then
  if not UnStopable then
  if not TBullet(Sprite).UnStopable  then
  stopped:=true;

  if Sprite is TPlayer then
  if Sprite<>Sender then
  begin
    if not stopped then
    begin
      if Weapon.Flying then
      Modif:=Tick/255
      else
      if Weapon.StartImpuls then
      Modif:=Speed/Weapon.MaxSpeed
      else
      Modif:=1;

      if (Weapon.WeaponClass=WC_Gun)and(Player.Perk[6])and(random<0.25) then
      modif:=modif*3;

      if Weapon.UnStopable then
      with TGameScene(Scene) do      
      modif:=modif*GameSpeed;

      TPlayer(Sprite).Health:=TPlayer(Sprite).Health-Weapon.Damage*modif;

      if Weapon.Fire then
      Sprite.SetColor(Max(Sprite.Red-round(5*Modif),Min(40,Sprite.Red)),Max(
        Sprite.Green-round(5*Modif),Min(40,Sprite.Green)),Max(Sprite.Blue-round(
          5*Modif),Min(40,Sprite.Blue)));

      if not (Weapon.WeaponClass=WC_Fire) then
      if Weapon.Damage>0 then
      begin
        if UnStopable then
        with TGameScene(Scene) do
        begin
          RenderImageRot(Sprite.x,Sprite.Y,random*256/40.6,
            1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
            cColorAlpha4(clWhite,round(GameSpeed)),fxBlend);
          RenderImageRot(Sprite.x,Sprite.Y,random*256/40.6,
            1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
            cColorAlpha4(clWhite,round(GameSpeed)),fxBlend);
        end
        else
        with TGameScene(Scene) do
        begin
          RenderImageRot(Sprite.x,Sprite.Y,random*256/40.6,
            1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
            cColorAlpha4(clWhite,150),fxBlend);
          RenderImageRot(Sprite.x,Sprite.Y,random*256/40.6,
            1-random*0.3+random*0.3,1-random*0.3+random*0.3,'blood_decor',0,
            cColorAlpha4(clWhite,150),fxBlend);
        end;

        for i:=0 to round(Min(Weapon.Damage/20,4)) do
        begin
          with TGameScene(Scene) do
          if UnStopable then
          RenderImageRot(
          Sprite.x-20*(i+1)*Sin256(Direction*40.6),Sprite.Y+20*(i+1)*
          Cos256(Direction*40.6),
          random*256/40.6,1-random*0.6,1-random*0.6,'blood_decor',0,
          cColorAlpha4(clWhite,Max(0,round(GameSpeed)-(i+1)*20)),fxBlend)
          else
          RenderImageRot(
          Sprite.x-20*(i+1)*Sin256(Direction*40.6),Sprite.Y+20*(i+1)*
          Cos256(Direction*40.6),
          random*256/40.6,1-random*0.6,1-random*0.6,'blood_decor',0,
          cColorAlpha4(clWhite,150-(i+1)*20),fxBlend);
        end;
        
        if not Weapon.UnStopable then
        if not Weapon.Ricoshet then
        self.stopped:=true;
      end;
    end;
  end;

  if sprite is TStaticObject then
  begin
    if not stopped then
    begin
      if Weapon.Flying then
      Modif:=Tick/255
      else
      if Weapon.StartImpuls then
      Modif:=Speed/Weapon.MaxSpeed
      else
      Modif:=1;

      if Weapon.UnStopable then
      with TGameScene(Scene) do      
      modif:=modif*GameSpeed;

      if not TStaticObject(Sprite).Undestructable then
      TStaticObject(Sprite).Hit(Weapon.Damage*modif,Point2(x,y),Weapon.WeaponClass,
        Weapon.WeaponType,false,Direction);

      if not Weapon.UnStopable then
      if not Weapon.Ricoshet then
      self.stopped:=true;
    end;
  end;

  if sprite is TNPC then
  begin
    if not stopped then
    begin
      if Weapon.Flying then
      Modif:=Tick/255
      else
      if Weapon.StartImpuls then
      Modif:=Speed/Weapon.MaxSpeed
      else
      Modif:=1;

      if Player<>nil then
      if (Weapon.WeaponClass=WC_Gun)and(Player.Perk[6])and(random<0.25) then
      Modif:=Modif*3;
      
      if Weapon.UnStopable then
      with TGameScene(Scene) do      
      modif:=modif*GameSpeed;

      TNPC(Sprite).Hit(Weapon.Damage*modif,Point2(x,y),Weapon.WeaponClass,
        Weapon.WeaponType,Sender is TPlayer,angle);

      if (Weapon.Stunning>0) and (random<Weapon.StunningChance)then
      TNPC(Sprite).StoppedTime:=TNPC(Sprite).StoppedTime+Weapon.Stunning;

      TNPC(Sprite).Poisoned:=Weapon.Poisoning;
      TNPC(Sprite).PoisonSpeed:=Weapon.PoisonSpeed;

      if Weapon.FlashFire then
      with TBullet.Create(Engine,WeaponStorage.Weapon['SmallFlame'],x,y,random*
        Pi*2,0,nil,Point2(0,0)) do
      UnStopable:=true;

      if Weapon.Fire then
      Sprite.SetColor(Max(Sprite.Red-round(2*Modif),Min(70,Sprite.Red)),
        Max(Sprite.Green-round(2*Modif),Min(70,Sprite.Green)),Max(Sprite.Blue-
        round(2*Modif),Min(70,Sprite.Blue)));

      if not UnStopable then
      if not Weapon.Ricoshet then
      self.stopped:=true;
    end;
  end;
end;

procedure TBullet.SetWeapon(const Value:TWeapon);
begin
  if FWeapon<>nil then
  FWeapon.Free;
  FWeapon:=TWeapon.Create;
  FWeapon.CopyValuesFrom(Value);
end;

procedure DoExplosion(ax,ay:real; ExplosionRadius,
   Force:real; Damage:real; WeaponType: cardinal);
var
modif:real;
i:integer;
z:real;
begin
  modif:=1;
  for i:=0 to SpriteEngine.Count-1 do
  if SpriteEngine[i] is TPhisicObject then
  begin
    if LineLength(ax,ay,SpriteEngine[i].x,SpriteEngine[i].y)<ExplosionRadius then
    begin
      if SpriteEngine[i] is TNPC then
      TNPC(SpriteEngine[i]).Hit(Damage*(ExplosionRadius-LineLength(ax,ay,
        SpriteEngine[i].x,SpriteEngine[i].y))/ExplosionRadius*Modif,
        Point2(SpriteEngine[i].X,SPriteEngine[i].Y),WC_Explosion,WeaponType
        ,true,random*256)
      else
      TPhisicObject(SpriteEngine[i]).Health:=TPhisicObject(SpriteEngine[
        i]).Health-Damage*(ExplosionRadius-LineLength(ax,ay,SpriteEngine[i].x,
        SpriteEngine[i].y))/ExplosionRadius*Modif;
    end;
  end;

  with TParticleSprite.Create(SpriteEngine) do
  begin
    x:=ax;
    y:=ay;
    ImageName:='explosion1';
    SetAnim(ImageName,0,30,0.5,false);
    ScaleX:=ExplosionRadius/64;
    ScaleY:=ExplosionRadius/64;

    DrawFx:=fxAdd;

    LifeTime:=30;
    Decay:=0.5;
    z:=1;
  end;

  with TStaticObject.Create(SpriteEngine,ax,ay,true,'Wave',false,0,0,5) do
  begin
    DoCollision:=false;

    Coloring:=true;
    StartAlpha:=255;
    EndAlpha:=20;
    StartRed:=255;
    StartGreen:=255;
    StartBlue:=150;
    EndRed:=255;
    EndGreen:=255;
    EndBlue:=150;

    Scaling:=true;
    ScaleX:=0;
    ScaleY:=0;
    StartScaleX:=0;
    StartScaleY:=0;
    EndScaleX:=ExplosionRadius/64;
    EndScaleY:=ExplosionRadius/64;
  end;
  
  for i := 0 to SpriteEngine.Count-1 do
  if SpriteEngine[i].DoCollision then
  if SpriteEngine[i] is TPhisicObject then
  if TPhisicObject(SpriteEngine[i]).Mass>0 then
  if not (SpriteEngine[i] is TBullet) then
  if PtInCircle(SpriteEngine[i].x,SpriteEngine[i].y,ax,ay,ExplosionRadius) then
  TPhisicObject(SpriteEngine[i]).AddForce(VecScale2(VecNorm2(
    VecSub2(Point2(SpriteEngine[i].x,SpriteEngine[i].y),Point2(ax,ay))),
    Force*(ExplosionRadius-LengthLine(ax,ay,SpriteEngine[i].x,SpriteEngine[i].y))/
    ExplosionRadius));

  for i := 0 to 10 do
  with TStaticObject.Create(SpriteEngine,ax,ay,false,'oblomki_decor') do
  begin
    ScaleX:=0.3;
    ScaleY:=0.3;
    incDeath:=5;
    Mass:=10;
    UnStopable:=true;
    CollideRadius:=1;
    DoCollision:=true;
    Particles:=true;
    ParticleSScaleX:=0;
    ParticleSScaleY:=0;
    ParticleEScaleX:=1;
    ParticleEScaleY:=1;

    ParticleSColor:=$FFFFFFFF;
    ParticleEColor:=$0000FF20;

    ParticleImage:='fire';
    ParticleSpeed:=1;
    ParticleDeathSpeed:=8;
    ParticleFx:=fxAdd;
    AddForce(VecScale2(Point2(random*20-random*20,random*20-random*20),Mass));
    z:=1;
  end;
end;

initialization
RegisterClass(TNPC);
RegisterClass(TStaticObject);

end.



